package snu.bi.MSAgent.menuHN;

import java.util.Random;
import java.util.Vector;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;

import snu.bi.MSAgent.DB.MSADBHandler;

public class HN {
	String dbhntimetable_name = "hnupdatetime";
	String dbhnweighttable_name = "hnedges";	
	edgeset eset;
	Context context;
	
	public HN(Context c) {
		eset = new edgeset(c);
		context = c;
		//eset.dbclear();
		eset.dbread();
	}
	public boolean needlearning() {
		boolean result = false;
		Long one_week_before = new java.util.Date().getTime()-7*86400*1000;
		
		MSADBHandler thisdb = MSADBHandler.open(context);				
		Cursor cur = thisdb.rawQuery("SELECT time FROM hnupdatetime where time > "+one_week_before);		
		if(cur.getCount() == 0) result = true;
		cur.close();
		thisdb.close();
		
		return result;		
	}
	public int[] make_menu(double cal) {
		eset.dbread();
		
		int cue;
		int[] re = new int[30];
		for(int i=0; i<30; i++) re[i] = -1;
		
		re[0] = choice_first_item(eset);
		cue = re[0];
		
		for(int i=1; i<30; i++) {
			if(i>0 && sum_cal(re) > cal*0.85) break;
			re[i] = get_nextmenu(cue, re);					
			cue = re[i];
			if(cue == 0) break;
		}	
		
		return re;
	}
	int choice_first_item(edgeset eset) {
		int[][] item = new int[10000][2];
		double[] weights = new double[10000];
		
		for(int i=0; i<10000; i++) {
			item[i][0] = i;
			item[i][1] = 0;
			weights[i] = 0;
		}
		
		Vector<Integer> set = eset.find_edge(0);
		int n1, n2;
		double we;
		
		for(int i=0; i<set.size(); i++) {
			n1 = eset.eset.elementAt(set.elementAt(i)).node1;
			n2 = eset.eset.elementAt(set.elementAt(i)).node2;
			we = eset.eset.elementAt(set.elementAt(i)).weight;
			
			weights[n1] = we;
			weights[n2] = we;
		}
		
		return get_one_random(weights, item);		
	}
	double sum_cal(int[] set) {
		double re = 0.0;		
		MSADBHandler thisdb = MSADBHandler.open(context);
		
		for(int i=0; i<set.length; i++) {
			if(set[i] == -1 || set[i] == 0) break;
			Cursor c=thisdb.rawQuery("select calories from menu where _id = "+set[i]);			
			c.moveToFirst();			
			re = re + c.getDouble(0);
			c.close();
		}
		
		thisdb.close();
		return re;
	}
	int get_nextmenu(int cue, int[] sset) {
		int re = 0;
				
		Vector<Integer> set = eset.find_edge(cue);
		double[] weight_set = new double[set.size()];
		int[][] menu_set = new int[set.size()][2];
		
		for(int i=0; i<set.size(); i++) {
			if(eset.eset.elementAt(set.elementAt(i)).node1 == cue) {
				menu_set[i][0] = eset.eset.elementAt(set.elementAt(i)).node2;
				menu_set[i][1] = 0;
			}
			else if(eset.eset.elementAt(set.elementAt(i)).node2 == cue) {
				menu_set[i][0] = eset.eset.elementAt(set.elementAt(i)).node1;
				menu_set[i][1] = 0;
			}
			else {
				menu_set[i][0] = eset.eset.elementAt(set.elementAt(i)).node1;
				menu_set[i][1] = eset.eset.elementAt(set.elementAt(i)).node2;
			}
			weight_set[i] = eset.eset.elementAt(set.elementAt(i)).weight;
		}
		int[][] notsame_menu = notsame(menu_set, sset);
		
		int flag = -1;		
		flag = get_one_random(weight_set, notsame_menu);
		
		if(flag == -1) {
			//Code for all exist
			System.out.println("all exist!!!!!!!!!!!");		
			return re;
		}
		
		if(cue == 0){
			if(notsame_menu[flag][0] == 0) re = menu_set[flag][1];
			if(notsame_menu[flag][1] == 0) re = menu_set[flag][0];
			
			int[] menu1 = {menu_set[flag][0]};
			int[] menu2 = {menu_set[flag][1]};
			
			//if(((sum_cal(menu1) < sum_cal(menu2)) == (sset[0] == -1))) re = menu_set[flag][0];
			if(sum_cal(menu1) < sum_cal(menu2)) re = menu_set[flag][0];
			else re = menu_set[flag][1];			
		}
		else re = menu_set[flag][0];
		
		return re;
	}
	int[][] notsame(int[][] menu, int[] sset) {
		int[][] result = menu;	
		
		for(int i=0; i<sset.length; i++) {
			if(sset[i] == -1) break;
			else {
				for(int j=0; j<menu.length; j++) {
					if(menu[j][0] == sset[i]) menu[j][0] = 0;					
					if(menu[j][1] == sset[i]) menu[j][1] = 0;					
				}
			}
		}		
		return result;
	}
	boolean existor(int temp, int[] set) {
		for(int i=0; i<set.length; i++) {
			if(temp == set[i]) return true;
		}
		return false;
	}
	int get_one_random(double[] weight_set, int[][] notsame_menu) {
		double weight_sum = 0.0;
		for(int i=0; i<weight_set.length; i++) if(notsame_menu[i][0] !=0 || notsame_menu[i][0] !=0) weight_sum += weight_set[i];
		if(weight_sum == 0.0) return -1;
				
		Random oRandom = new Random(new java.util.Date().getTime());
		Double percent = oRandom.nextDouble();
		
		int flag = 0;
		for(int i=0; i<weight_set.length; i++) {
			if(notsame_menu[i][0] !=0 || notsame_menu[i][0] !=0) {
				weight_set[i] = weight_set[i]/weight_sum;
				if(weight_set[i] >= percent) {
					flag = i;
					break;
				}
				percent -= weight_set[i]; 
			}
		}
		return flag;
	}
	public void learning() {
		MSADBHandler thisdb = MSADBHandler.open(context);
		
		String last_time = getmaxtimeindb(thisdb, dbhntimetable_name) ;
		
		Cursor c = thisdb.rawQuery("select * from menu_log where time > "+last_time);		
		
		Vector<int[]> table = make_Vector(c);
		eset.edgeclear();
		for(int i=0; i<table.size(); i++) {
			System.out.println(i);
			int[] temp_menu = table.elementAt(i);
			//if man eat just one item, then we cannot learn that case. But ignore that.			
			for(int j=0; j<30; j++) {
				if(temp_menu[j] == -1) break;
				for(int k=j+1; k<30; k++) {
					if(temp_menu[k] == -1) break;
					eset.weight_up(temp_menu[j], temp_menu[k]);
				}
			}
		}
		
		c.close();
		thisdb.close();
		eset.dbwrite();
	}	
	private String getmaxtimeindb(MSADBHandler sdb, String tablename) {    	
		Cursor cur = sdb.rawQuery("SELECT MAX(time) FROM "+tablename);
		cur.moveToFirst();
		String re = cur.getString(0);
		cur.close();
		return re;
	}
	Vector<int[]> make_Vector(Cursor c) {
		Vector<int[]> re = new Vector<int[]>();
		long time = 0;
		int count = 0;
		
		c.moveToFirst();
		
		for(int i=0; i<c.getCount(); i++) {
			if(time != c.getLong(0)) {
				time = c.getLong(0);
				count = 0;
				
				int[] v = new int[30];
				for(int j=0; j<30; j++) v[j] = -1;
				
				v[count] = c.getInt(1);
				re.add(v);
				count++;
			}
			else {
				if(count < 30) {
					re.elementAt(re.size()-1)[count] = c.getInt(1);
					count++;
				}
			}
			if(i!=c.getCount()-1) c.moveToNext();
		}		
		
		return re;
	}
}
class edgeset{
	//int pop_size = 10000;
	String dbhntimetable_name = "hnupdatetime";
	String dbhnweighttable_name = "hnedges";	
	Vector <edge> eset;
	Context context;
	edgeset(Context c) {
		eset = new Vector<edge>();		
		//add_edge(new edge(0,0,0));
		context = c;
	}
	void add_edge(edge e) {
		eset.add(e);		
	}
	void dbwrite() {
		MSADBHandler thisdb = MSADBHandler.open(context);
		
		Cursor c = thisdb.rawQuery("select * from hnedges");
		int temp = c.getCount();
		dbclear();		
		c.close();
		
		for(int i=0; i<eset.size(); i++) thisdb.insert(dbhnweighttable_name, null, eset.elementAt(i).getContentValues());
		
		c = thisdb.rawQuery("select * from hnedges");
		temp = c.getCount();
		
		ContentValues cv = new ContentValues();
		cv.put("time", new java.util.Date().getTime());
		thisdb.insert(dbhntimetable_name, null, cv);
		
		c.close();
		thisdb.close();
	}
	void dbread() {
		eset.clear();
		MSADBHandler thisdb = MSADBHandler.open(context);
		
		Cursor c = thisdb.rawQuery("select * from hnedges");
		c.moveToFirst();
		for(int i=0; i<c.getCount(); i++) {
			eset.add(new edge(c.getInt(1), c.getInt(2), c.getDouble(0)));
			if(i!=c.getCount()-1) c.moveToNext();
		}
		
		c.close();
		thisdb.close();
	}
	void dbclear() {
		MSADBHandler thisdb = MSADBHandler.open(context);
				
		thisdb.clear_table(dbhnweighttable_name);
				
		thisdb.close();
	}
	void edgeclear() {
		eset.clear();
	}
	Vector<Integer> find_edge(int n1) {
		Vector<Integer> set = new Vector<Integer>();
				
		
			for(int i=0; i<eset.size(); i++) {
				if(n1 == 0) {
					set.add(i);
				}
				else if(n1 == eset.elementAt(i).node1 || n1 == eset.elementAt(i).node2) {
					set.add(i);
				}
			}		
		
		return set;		
	}
	int find(int n1, int n2) {
		int n11 = n1; int n22 = n2;
		if (n1 > n2) {
			int temp = n11;
			n11 = n2;
			n22 = temp;
		}
		for(int i=0; i<eset.size(); i++) {
			if(n11 == eset.elementAt(i).node1 && n22 == eset.elementAt(i).node2) {
				return i;
			}
		}
		return -1;
	}
	void weight_up(int n1, int n2) {
		if(n1 == n2) {
			return;
		}
		int temp = find(n1, n2);
		if(temp != -1) {
			eset.elementAt(temp).weightup();
		}
		else {
			add_edge(new edge(n1, n2));
			eset.lastElement().weightup();
		}
	}	
}
class edge{
	double weight;
	int node1, node2;
	
	edge() {
		
	}
	edge(int n1, int n2, double w) {
		if (n1<n2) {
			weight = w; node1 = n1; node2 = n2;
		}
		else if(n1>n2) {
			weight = w; node1 = n2; node2 = n1;
		}
		else {
			weight = w; node1 = 0; node2 = 0;
		}		
	}
	edge(int n1, int n2) {
		if (n1<n2) {
			weight = 0; node1 = n1; node2 = n2;
		}
		else if(n1>n2) {
			weight = 0; node1 = n2; node2 = n1;
		}
		else {
			weight = 0; node1 = 0; node2 = 0;
		}	
	}
	ContentValues getContentValues() {
		ContentValues cv = new ContentValues();
		cv.put("weight", weight);
		cv.put("node1", node1);
		cv.put("node2", node2);
		return cv;
	}
	void weightup() {
		weight++;
	}
}
